package fr.inria.phoenix.diasuite.framework.context.presence;

import fr.inria.diagen.core.ServiceConfiguration;
import fr.inria.diagen.core.exception.InvocationException;
import fr.inria.diagen.core.service.composite.CompositeException;
import fr.inria.diagen.core.network.RemoteServiceInfo;
import fr.inria.diagen.core.service.local.Service;
import fr.inria.diagen.core.service.proxy.Proxy;

import fr.inria.phoenix.diasuite.framework.device.motiondetector.MotionFromMotionDetector;

/**
 * <pre>
context Presence as Boolean{
 * 	when provided motion from MotionDetector
 * 	always publish;
 * }
</pre>
 */
@SuppressWarnings("all")
public abstract class AbstractPresence extends Service {
    
    public AbstractPresence(ServiceConfiguration serviceConfiguration) {
        super("/Context/Presence/", serviceConfiguration);
    }
    
    // Methods from the Service class
    @Override
    protected final void internalPostInitialize() {
        postInitialize();
    }
    
    @Override
    protected void postInitialize() {
        // Default implementation of post initialize: subscribe to all required devices
        discoverMotionDetectorForSubscribe.all().subscribeMotion(); // subscribe to motion from all MotionDetector devices
    }
    
    @Override
    public final void valueReceived(java.util.Map<String, Object> properties, RemoteServiceInfo source, String eventName, Object value, Object... indexes) {
        if (eventName.equals("motion") && source.isCompatible("/Device/Device/PhysicalDevice/Sensor/MotionDetector/")) {
            MotionFromMotionDetector motionFromMotionDetector = new MotionFromMotionDetector(this, source, (java.lang.Boolean) value);
            
            setPresence(onMotionFromMotionDetector(motionFromMotionDetector));
        }
    }
    
    @Override
    public final Object getValueCalled(java.util.Map<String, Object> properties, RemoteServiceInfo source, String valueName,
            Object... indexes) throws Exception {
        if (valueName.equals("presence")) {
            return getLastValue();
        }
        throw new InvocationException("Unsupported method call: " + valueName);
    }
    // End of methods from the Service class
    
    // Code relative to the return value of the context
    private java.lang.Boolean contextValue;
    
    private void setPresence(java.lang.Boolean newContextValue) {
        contextValue = newContextValue;
        getProcessor().publishValue(getOutProperties(), "presence", newContextValue);
    }
    
    /**
     * Get the last value of the context
     * 
     * @return the latest value published by the context
     */
    protected final java.lang.Boolean getLastValue() {
        return contextValue;
    }
    // End of code relative to the return value of the context
    
    // Interaction contract implementation
    /**
     * This method is called when a <code>MotionDetector</code> device on which we have subscribed publish on its <code>motion</code> source.
    
    <pre>
    when provided motion from MotionDetector
     * 	always publish;
    </pre>
     * 
     * @param motionFromMotionDetector the value of the <code>motion</code> source and the <code>MotionDetector</code> device that published the value.
     * @return the value to publish
     */
    protected abstract java.lang.Boolean onMotionFromMotionDetector(MotionFromMotionDetector motionFromMotionDetector);
    
    // End of interaction contract implementation
    
    // Discover part for MotionDetector devices
    /**
     * Use this object to discover MotionDetector devices.
    
    <pre>
    device MotionDetector extends Sensor {
    	source motion as Boolean;
    }
    </pre>
     * 
     * @see MotionDetectorDiscoverer
     */
    protected final MotionDetectorDiscoverer discoverMotionDetectorForSubscribe = new MotionDetectorDiscoverer(this);
    
    /**
     * Discover object that will exposes the <code>MotionDetector</code> devices that can be discovered
    
    <pre>
    device MotionDetector extends Sensor {
    	source motion as Boolean;
    }
    </pre>
     */
    protected final static class MotionDetectorDiscoverer {
        private Service serviceParent;
        
        private MotionDetectorDiscoverer(Service serviceParent) {
            super();
            this.serviceParent = serviceParent;
        }
        
        private MotionDetectorComposite instantiateComposite() {
            return new MotionDetectorComposite(serviceParent);
        }
        
        /**
         * Returns a composite of all accessible <code>MotionDetector</code> devices
         * 
         * @return a {@link MotionDetectorComposite} object composed of all discoverable <code>MotionDetector</code>
         */
        public MotionDetectorComposite all() {
            return instantiateComposite();
        }
        
        /**
         * Returns a proxy to one out of all accessible <code>MotionDetector</code> devices
         * 
         * @return a {@link MotionDetectorProxy} object pointing to a random discoverable <code>MotionDetector</code> device
         */
        public MotionDetectorProxy anyOne() {
            return all().anyOne();
        }
        
        /**
         * Returns a composite of all accessible <code>MotionDetector</code> devices whose attribute <code>id</code> matches a given value.
         * 
         * @param id The <code>id<code> attribute value to match.
         * @return a {@link MotionDetectorComposite} object composed of all matching <code>MotionDetector</code> devices
         */
        public MotionDetectorComposite whereId(java.lang.String id) throws CompositeException {
            return instantiateComposite().andId(id);
        }
        
        /**
         * Returns a composite of all accessible <code>MotionDetector</code> devices whose attribute <code>location</code> matches a given value.
         * 
         * @param location The <code>location<code> attribute value to match.
         * @return a {@link MotionDetectorComposite} object composed of all matching <code>MotionDetector</code> devices
         */
        public MotionDetectorComposite whereLocation(java.lang.String location) throws CompositeException {
            return instantiateComposite().andLocation(location);
        }
        
        /**
         * Returns a composite of all accessible <code>MotionDetector</code> devices whose attribute <code>user</code> matches a given value.
         * 
         * @param user The <code>user<code> attribute value to match.
         * @return a {@link MotionDetectorComposite} object composed of all matching <code>MotionDetector</code> devices
         */
        public MotionDetectorComposite whereUser(java.lang.String user) throws CompositeException {
            return instantiateComposite().andUser(user);
        }
    }
    
    /**
     * A composite of several <code>MotionDetector</code> devices
    
    <pre>
    device MotionDetector extends Sensor {
    	source motion as Boolean;
    }
    </pre>
     */
    protected final static class MotionDetectorComposite extends fr.inria.diagen.core.service.composite.Composite<MotionDetectorProxy> {
        private MotionDetectorComposite(Service serviceParent) {
            super(serviceParent, "/Device/Device/PhysicalDevice/Sensor/MotionDetector/");
        }
        
        @Override
        protected MotionDetectorProxy instantiateProxy(RemoteServiceInfo rsi) {
            return new MotionDetectorProxy(service, rsi);
        }
        
        /**
         * Returns this composite in which a filter was set to the attribute <code>id</code>.
         * 
         * @param id The <code>id<code> attribute value to match.
         * @return this {@link MotionDetectorComposite}, filtered using the attribute <code>id</code>.
         */
        public MotionDetectorComposite andId(java.lang.String id) throws CompositeException {
            filterByAttribute("id", id);
            return this;
        }
        
        /**
         * Returns this composite in which a filter was set to the attribute <code>location</code>.
         * 
         * @param location The <code>location<code> attribute value to match.
         * @return this {@link MotionDetectorComposite}, filtered using the attribute <code>location</code>.
         */
        public MotionDetectorComposite andLocation(java.lang.String location) throws CompositeException {
            filterByAttribute("location", location);
            return this;
        }
        
        /**
         * Returns this composite in which a filter was set to the attribute <code>user</code>.
         * 
         * @param user The <code>user<code> attribute value to match.
         * @return this {@link MotionDetectorComposite}, filtered using the attribute <code>user</code>.
         */
        public MotionDetectorComposite andUser(java.lang.String user) throws CompositeException {
            filterByAttribute("user", user);
            return this;
        }
        
        /**
         * Subscribes to the <code>motion</code> source. After a call to this method, the context will be notified when a
         * <code>MotionDetector</code> device of this composite publishes a value on its <code>motion</code> source.
         */
        public void subscribeMotion() {
            subscribeValue("motion");
        }
        
        /**
         * Unsubscribes from the <code>motion</code> source. After a call to this method, the context will no more be notified
         * when a <code>MotionDetector</code> device of this composite publishes a value on its <code>motion</code> source.
         */
        public void unsubscribeMotion() {
            unsubscribeValue("motion");
        }
    }
    
    /**
     * A proxy to one <code>MotionDetector</code> device
    
    <pre>
    device MotionDetector extends Sensor {
    	source motion as Boolean;
    }
    </pre>
     */
    protected final static class MotionDetectorProxy extends Proxy {
        private MotionDetectorProxy(Service service, RemoteServiceInfo remoteServiceInfo) {
            super(service, remoteServiceInfo);
        }
        
        /**
         * Subscribes to the <code>motion</code> source. After a call to this method, the context will be notified when the
         * <code>MotionDetector</code> device of this proxy publishes a value on its <code>motion</code> source.
         */
        public void subscribeMotion() {
            subscribeValue("motion");
        }
        
        /**
         * Unsubscribes from the <code>motion</code> source. After a call to this method, the context will no more be notified
         * when the <code>MotionDetector</code> device of this proxy publishes a value on its <code>motion</code> source.
         */
        public void unsubscribeMotion() {
            unsubscribeValue("motion");
        }
        
        /**
         * @return the value of the <code>id</code> attribute
         */
        public java.lang.String id() {
            return (java.lang.String) callGetValue("id");
        }
        
        /**
         * @return the value of the <code>location</code> attribute
         */
        public java.lang.String location() {
            return (java.lang.String) callGetValue("location");
        }
        
        /**
         * @return the value of the <code>user</code> attribute
         */
        public java.lang.String user() {
            return (java.lang.String) callGetValue("user");
        }
    }
    // End of discover part for MotionDetector devices
}
